package se.webbzon.oschi01.worldeditor;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import se.webbzon.oschi01.worldobject.WorldObject;

public class WorldObjectCloner {
	
	private final WorldObjectInflatorGroup inflatorGroup;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public WorldObjectCloner(WorldObjectInflatorGroup inflatorGroup) {
		this.inflatorGroup = inflatorGroup;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	public Collection<WorldObject> clone(Collection<DeflatableWorldObject> instances) 
			throws WorldObjectClonerException {
		// A tracker used to track circular dependencies
		HashSet<DeflatableWorldObject> tracker = new HashSet<DeflatableWorldObject>();
		// A map used to contain the cloned instances
		HashMap<DeflatableWorldObject,WorldObject> dependencyMap = 
				new HashMap<DeflatableWorldObject,WorldObject>();
		
		// Clone all instances
		for (DeflatableWorldObject instance : instances) {
			if (!tracker.contains(instance))
				cloneInner(instance,dependencyMap,tracker);
		}
		
		return dependencyMap.values();
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	private WorldObject cloneInner(	DeflatableWorldObject instance, 
									HashMap<DeflatableWorldObject,WorldObject> dependencyMap,
									HashSet<DeflatableWorldObject> tracker) 
									throws WorldObjectClonerException {
		// Return the clone if it exists
		WorldObject worldObject = dependencyMap.get(instance);
		if (worldObject != null)
			return worldObject;
		
		// Check for circular dependencies
		if (tracker.contains(instance))
			throw new WorldObjectClonerException(	"Tracked circular dependencies "+
													"in world object with name \"" +
													instance.getName() + "\".");
		tracker.add(instance);
		
		// Find the inflator
		WorldObjectInflator inflator = inflatorGroup.getInflator(instance.getName());
		if (inflator == null)
			throw new WorldObjectClonerException(	"No inflator was found which "+
													"supprots creation of instances"+
													" with the name \"" + 
													instance.getName() + "\".");
		
		final WorldObjectDeflation deflation = instance.getDeflation();
		DeflatableWorldObject[] rawDependencies = 
				new DeflatableWorldObject[deflation.trueDependencies()];
		for (int i = 0; i < rawDependencies.length; i++)
			rawDependencies[i] = (DeflatableWorldObject) deflation.getTrueDependency(i);
		WorldObject[] dependencies;
		
		// Extract dependencies
		dependencies = new WorldObject[rawDependencies.length];
		for (int i = 0; i < dependencies.length; i++)
			dependencies[i] = cloneInner(	rawDependencies[i],
					dependencyMap,
					tracker);
		
		// Put the cloned world object into the map
		worldObject = inflator.makeWorldObject(deflation);
		dependencyMap.put(instance,worldObject);
		return worldObject;
	}

}
